[EuiStepsHorizontal][A11y] Prevent screen reader duplication#9574
Conversation
- this prevents reading out duplicate text content
There was a problem hiding this comment.
Pull request overview
This PR improves EuiStepsHorizontal / EuiStepHorizontal screen reader output by replacing the title attribute with aria-label to prevent duplicate announcements.
Changes:
- Replaced the
titleattribute onEuiStepHorizontal’s<button>witharia-label. - Updated Jest snapshots to reflect the new attribute output.
- Added an upcoming changelog entry documenting the accessibility fix.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/eui/src/components/steps/step_horizontal.tsx | Switches from title to aria-label on the step button to prevent duplicate SR output |
| packages/eui/src/components/steps/snapshots/steps_horizontal.test.tsx.snap | Snapshot updates for EuiStepsHorizontal button attributes |
| packages/eui/src/components/steps/snapshots/step_horizontal.test.tsx.snap | Snapshot updates for EuiStepHorizontal button attributes |
| packages/eui/changelogs/upcoming/9574.md | Changelog entry for the accessibility improvement |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| aria-disabled={status === 'disabled' ? true : undefined} | ||
| className={classes} | ||
| title={titleAttr} | ||
| aria-label={titleAttr} | ||
| onClick={onStepClick} | ||
| disabled={disabled} |
There was a problem hiding this comment.
aria-disabled is set based on status === 'disabled', but the actual disabled behavior is controlled only by the disabled prop (disabled={disabled} and onStepClick only guards on disabled). This allows a status="disabled" step to still be clickable/activatable despite being announced as disabled. Consider using a single computed boolean (e.g., isDisabled = disabled || status === 'disabled') for aria-disabled, the disabled attribute, and the click guard so behavior matches the a11y state.
There was a problem hiding this comment.
It's not due to the changes in this PR. There might be benefit to having the button focusable if disabled, so I wouldn't change the aria-disabled vs disabled behavior here at this point. What we can do though is guard the onClick on both to prevent it from being being fired if only status === 'disabled' is passed without disabled.
Updated in 0fa990a
- additionally renames the map to fit aria-label usage better
weronikaolejniczak
left a comment
There was a problem hiding this comment.
Tested with NVDA/Chrome, JAWS/Chrome and VO/Safari. There's no duplication ✅
2 doubts (one in a separate comment):
❓ I'm all for removing the title attr but I'm wondering if we should replace it with an EuiToolTip or not... What do you think?
@weronikaolejniczak I'm not sure if it has too much value. 🤔 Yes, the |
weronikaolejniczak
left a comment
There was a problem hiding this comment.
Thanks for addressing my doubts, Lene! I agree the tooltip would be an enhancement, let's leave it for now. And thanks for updating the changelog!
🟢
💚 Build SucceededHistory
cc @mgadewoll |
💚 Build Succeeded
History
cc @mgadewoll |
## Dependency updates - `@elastic/eui` - v114.2.0 ⏩ v114.3.0 --- ## Package updates ## [`v114.3.0`](https://github.com/elastic/eui/releases/v114.3.0) - Updated `productDashboard` icon. ([#9607](elastic/eui#9607)) - Updated `EuiStepsHorizontal` to prevent steps being clickable when `status="disabled"` ([#9574](elastic/eui#9574)) **Bug fixes** - Fixed broken SVG for `chartPie` icon. ([#9607](elastic/eui#9607)) - Fixed a bug in `EuiDataGrid` that caused the scroll position to reset when using middle mouse button to scroll the container. ([#9613](elastic/eui#9613)) - Fixed `EuiFlyout` to compare `pushMinBreakpoint` against the container's width, instead of the viewport width, when the `container` prop is provided. This ensures app-scoped flyouts switch between push and overlay modes based on the space actually available inside their container. ([#9592](elastic/eui#9592)) **Accessibility** - Fixed duplicate screen reader output for `EuiStepsHorizontal` ([#9574](elastic/eui#9574))
Summary
This PR updates
EuiStepsHorizontalto fix duplicate output for screen readers.The output for screen readers previously read:
titleattribute (though this is not consistently read across screen readers)title)Instead of using the
titleattribute we can usearia-labelinstead which results in only thearia-labelbeing read for the button element.titleisn't actually adding further benefit.API Changes
🟢 No API changes
Screenshots
screen reader output
video
Screen.Recording.2026-04-08.at.12.09.49.mov
Screen.Recording.2026-04-08.at.12.09.23.mov
Screen.Recording.2026-04-08.at.12.04.24.mov
Screen.Recording.2026-04-08.at.12.06.28.mov
Screen.Recording.2026-04-08.at.12.19.22.mov
Screen.Recording.2026-04-08.at.12.18.55.mov
Impact Assessment
Note: Most PRs should be tested in Kibana to help gauge their Impact before merging.
Impact level: 🟢 Low - Due to DOM changes, snapshots might need updating.
Release Readiness
Documentation: {link to docs page(s)}Figma: {link to Figma or issue}Migration guide: {steps or link, for breaking/visual changes or deprecations}Adoption plan (new features): {link to issue/doc or outline who will integrate this and where}QA instructions for reviewer
Checklist before marking Ready for Review
QA: Tested in CodeSandbox and KibanaQA: Tested docs changesBreaking changes: Addedbreaking changelabel (if applicable)Reviewer checklist